package com.google.jessewilson.codesearch.standalone;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import com.google.jessewilson.codesearch.api.*;
import com.google.jessewilson.codesearch.api.spi.SearchProviderBackend;
import com.google.jessewilson.codesearch.io.Path;
import com.google.jessewilson.codesearch.ui.ResultActionsProvider;
import com.google.jessewilson.codesearch.ui.ResultsTreeActions;
import com.google.jessewilson.codesearch.ui.tree.ResultsForOneQuery;
import com.google.jessewilson.codesearch.util.EventListSink;
import com.google.jessewilson.codesearch.util.Progress;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author jessewilson
 */
public class StandaloneCodeSearch {

  private final EventList<Line> lines;
  private final JPanel panel;

  private final ResultsTreeActions resultsTreeActions;
  private final ExecutorService executorService = Executors.newCachedThreadPool();

  public StandaloneCodeSearch() {

    lines = new BasicEventList<Line>();
    QueryResponse queryResponse = new DefaultQueryResponse(executorService,
        lines, new Query.Builder().build(), null);

    // the tree
    ResultsForOneQuery resultsForOneQuery = new ResultsForOneQuery(queryResponse);
    ResultActionsProvider actionsProvider = new StanaloneActionsProvider(executorService);
    resultsForOneQuery.addActionsProvider(actionsProvider);

    // a search box instead of code search
    JPanel searchPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    final JTextField searchField = new JTextField(15);
    searchField.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        search(new Query.Builder().searchTerm(searchField.getText()).build(), null);
      }
    });
    searchPane.add(new JLabel("Search:"));
    searchPane.add(searchField);

    // a progress bar
    final JProgressBar progressBar = new JProgressBar();
    progressBar.setIndeterminate(queryResponse.getProgress().isBusy());
    queryResponse.getProgress().addListener(new Progress.Listener() {
      public void progressUpdated(Progress progress) {
        progressBar.setIndeterminate(progress.isBusy());
      }
    });

    // action buttons
    resultsTreeActions = new ResultsTreeActions(resultsForOneQuery.getTreeList(),
        resultsForOneQuery.getTable(), actionsProvider, queryResponse);
    JButton expandAll = new JButton(new AbstractAction("Expand All") {
      public void actionPerformed(ActionEvent e) {
        resultsTreeActions.expandAll();
      }
    });
    JButton collapseAll = new JButton(new AbstractAction("Collapse All") {
      public void actionPerformed(ActionEvent e) {
        resultsTreeActions.collapseAll();
      }
    });
    JButton previous = new JButton(new AbstractAction("Previous") {
      public void actionPerformed(ActionEvent e) {
        resultsTreeActions.previous();
      }
    });
    JButton next = new JButton(new AbstractAction("Next") {
      public void actionPerformed(ActionEvent e) {
        resultsTreeActions.next();
      }
    });
    JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
    buttons.add(expandAll);
    buttons.add(collapseAll);
    buttons.add(previous);
    buttons.add(next);
    buttons.add(progressBar);

    // the panel
    panel = new JPanel(new BorderLayout());
    panel.add(resultsForOneQuery.getComponent(), BorderLayout.CENTER);
    panel.add(searchPane, BorderLayout.NORTH);
    panel.add(buttons, BorderLayout.SOUTH);
  }

  public void search(Query query, SearchProviderBackend searchProviderBackend) {
    lines.getReadWriteLock().writeLock().lock();
    try {
      lines.clear();
    } finally {
      lines.getReadWriteLock().writeLock().unlock();
    }

    try {
      QueryResponse queryResponse = new DefaultQueryResponse(executorService,
          lines, query, searchProviderBackend);
      searchProviderBackend.search(query,
          new EventListSink<Line>(queryResponse.getLines()), new Page(0, 100));
    } catch (SearchProviderException e) {
      Logger.getLogger(StandaloneCodeSearch.class.getName())
          .log(Level.WARNING, "Search failed", e);
    }

    // add some random results
    addLines();
  }

  private void addLines() {
    lines.getReadWriteLock().writeLock().lock();
    try {
      CodeFile codeFile;
      Line line;

      codeFile = new CodeFile();
      codeFile.setPath(Path.fromString("/usr/local/Main.java"));

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(13);
      line.setLineValue("public static void main");
      lines.add(line);

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(15);
      line.setLineValue("System.out.println(\"foo\"");
      lines.add(line);

      codeFile = new CodeFile();
      codeFile.setPath(Path.fromString("/usr/local/BackgroundTask.java"));

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(13);
      line.setLineValue("public static void foo()");
      lines.add(line);

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(15);
      line.setLineValue("System.out.println(\"bar\"");
      lines.add(line);

      codeFile = new CodeFile();
      codeFile.setPath(Path.fromString("ForegroundTask.java"));

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(13);
      line.setLineValue("public static void foreground()");
      lines.add(line);

      codeFile = new CodeFile();
      codeFile.setPath(Path.fromString("Folder/"));

      line = new Line();
      line.setCodeFile(codeFile);
      line.setLineNumber(13);
      line.setLineValue("public static void folder");
      lines.add(line);

    } finally {
      lines.getReadWriteLock().writeLock().unlock();
    }
  }

  public JPanel getComponent() {
    return panel;
  }

  public static void main(final String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        StandaloneCodeSearch standaloneCodeSearch = new StandaloneCodeSearch();

        // draw it all up on screen
        JFrame frame = new JFrame();
        frame.getContentPane().add(standaloneCodeSearch.getComponent());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        if (args.length == 1) {
            standaloneCodeSearch.search(new Query.Builder().searchTerm("html")
                .build(), null);
        }
      }
    });
  }

  private static class StanaloneActionsProvider implements ResultActionsProvider {
    private final ExecutorService executorService;

    public StanaloneActionsProvider(ExecutorService executorService) {
      this.executorService = executorService;
    }

    public List<Action> actions(final CodeFile codeFile, final Line line, final QueryResponse results) {
      List<Action> result = new ArrayList<Action>();
      result.add(new AbstractAction("Open " + codeFile.getPath().toString()) {
        public void actionPerformed(ActionEvent e) {
          doDefaultAction(codeFile, line, results);
        }
      });

      return result;
    }

    public void doDefaultAction(CodeFile codeFile, Line line, QueryResponse results) {
      FileInFrameViewer fileViewer = new FileInFrameViewer(
          executorService, codeFile.getSearchProvider());
      fileViewer.setVisible(true);
      fileViewer.open(codeFile, line, results);
    }
  }
}
